<template>
  <div v-if="hasError" class="error-boundary">
    <div class="error-container">
      <div class="error-icon">
        <el-icon :size="64">
          <WarningFilled />
        </el-icon>
      </div>
      
      <div class="error-content">
        <h2 class="error-title">{{ errorTitle }}</h2>
        <p class="error-message">{{ errorMessage }}</p>
        
        <div v-if="showDetails && errorDetails" class="error-details">
          <details>
            <summary>错误详情</summary>
            <pre class="error-stack">{{ errorDetails }}</pre>
          </details>
        </div>
        
        <div class="error-actions">
          <el-button type="primary" @click="handleRetry">
            {{ retryText }}
          </el-button>
          <el-button @click="handleReport">
            {{ reportText }}
          </el-button>
          <el-button @click="handleGoHome">
            {{ homeText }}
          </el-button>
        </div>
      </div>
    </div>
  </div>
  
  <slot v-else />
</template>

<script setup lang="ts">
import { ref, onErrorCaptured, onMounted, onUnmounted } from 'vue';
import { ElMessage } from 'element-plus';
import { WarningFilled } from '@element-plus/icons-vue';

/**
 * 错误信息接口
 */
interface ErrorInfo {
  message: string;
  stack?: string;
  componentStack?: string;
  errorBoundary?: string;
}

/**
 * 组件Props
 */
interface Props {
  fallback?: boolean;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
  onRetry?: () => void;
  onReport?: (error: Error, errorInfo: ErrorInfo) => void;
  errorTitle?: string;
  errorMessage?: string;
  retryText?: string;
  reportText?: string;
  homeText?: string;
  showDetails?: boolean;
  autoReport?: boolean;
  reportUrl?: string;
}

const props = withDefaults(defineProps<Props>(), {
  fallback: true,
  errorTitle: '页面出现错误',
  errorMessage: '抱歉，页面遇到了一些问题。请尝试刷新页面或联系技术支持。',
  retryText: '重试',
  reportText: '报告问题',
  homeText: '返回首页',
  showDetails: false,
  autoReport: false,
  reportUrl: '/api/error-report'
});

/**
 * 组件Emits
 */
const emit = defineEmits<{
  'error': [error: Error, errorInfo: ErrorInfo];
  'retry': [];
  'report': [error: Error, errorInfo: ErrorInfo];
}>();

/**
 * 响应式数据
 */
const hasError = ref(false);
const error = ref<Error | null>(null);
const errorInfo = ref<ErrorInfo | null>(null);
const errorDetails = ref('');

/**
 * 全局错误处理
 */
const handleGlobalError = (event: ErrorEvent): void => {
  const error = new Error(event.message);
  error.stack = event.error?.stack;
  
  const errorInfo: ErrorInfo = {
    message: event.message,
    stack: event.error?.stack,
    componentStack: 'Global Error',
    errorBoundary: 'Global'
  };
  
  handleError(error, errorInfo);
};

/**
 * Promise错误处理
 */
const handleUnhandledRejection = (event: PromiseRejectionEvent): void => {
  const error = event.reason instanceof Error ? event.reason : new Error(String(event.reason));
  
  const errorInfo: ErrorInfo = {
    message: error.message,
    stack: error.stack,
    componentStack: 'Promise Rejection',
    errorBoundary: 'Promise'
  };
  
  handleError(error, errorInfo);
};

/**
 * 处理错误
 */
const handleError = (err: Error, info: ErrorInfo): void => {
  hasError.value = true;
  error.value = err;
  errorInfo.value = info;
  
  // 生成错误详情
  errorDetails.value = [
    `错误信息: ${err.message}`,
    `错误堆栈: ${err.stack || '无'}`,
    `组件堆栈: ${info.componentStack || '无'}`,
    `错误边界: ${info.errorBoundary || '无'}`,
    `时间: ${new Date().toISOString()}`,
    `用户代理: ${navigator.userAgent}`,
    `URL: ${window.location.href}`
  ].join('\n');
  
  // 触发错误事件
  emit('error', err, info);
  props.onError?.(err, info);
  
  // 自动报告错误
  if (props.autoReport) {
    reportError(err, info);
  }
  
  // 开发环境下打印错误
  if (import.meta.env.DEV) {
    console.error('ErrorBoundary捕获到错误:', err, info);
  }
};

/**
 * 捕获组件错误
 */
onErrorCaptured((err: Error, instance: any, info: string) => {
  const errorInfo: ErrorInfo = {
    message: err.message,
    stack: err.stack,
    componentStack: info,
    errorBoundary: 'Component'
  };
  
  handleError(err, errorInfo);
  
  // 阻止错误继续传播
  return false;
});

/**
 * 重试
 */
const handleRetry = (): void => {
  hasError.value = false;
  error.value = null;
  errorInfo.value = null;
  errorDetails.value = '';
  
  emit('retry');
  props.onRetry?.();
};

/**
 * 报告问题
 */
const handleReport = (): void => {
  if (error.value && errorInfo.value) {
    emit('report', error.value, errorInfo.value);
    props.onReport?.(error.value, errorInfo.value);
    
    if (!props.onReport) {
      reportError(error.value, errorInfo.value);
    }
  }
};

/**
 * 返回首页
 */
const handleGoHome = (): void => {
  window.location.href = '/';
};

/**
 * 报告错误到服务器
 */
const reportError = async (err: Error, info: ErrorInfo): Promise<void> => {
  try {
    const errorData = {
      message: err.message,
      stack: err.stack,
      componentStack: info.componentStack,
      errorBoundary: info.errorBoundary,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent,
      url: window.location.href,
      userId: getCurrentUserId(),
      sessionId: getSessionId()
    };
    
    await fetch(props.reportUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(errorData)
    });
    
    ElMessage.success('错误报告已提交');
  } catch (reportError) {
    console.error('报告错误失败:', reportError);
    ElMessage.error('错误报告提交失败');
  }
};

/**
 * 获取当前用户ID
 */
const getCurrentUserId = (): string | null => {
  try {
    const userInfo = localStorage.getItem('userInfo');
    if (userInfo) {
      const user = JSON.parse(userInfo);
      return user.id || user.userId || null;
    }
  } catch {
    // 忽略解析错误
  }
  return null;
};

/**
 * 获取会话ID
 */
const getSessionId = (): string => {
  let sessionId = sessionStorage.getItem('sessionId');
  if (!sessionId) {
    sessionId = Date.now().toString(36) + Math.random().toString(36).substr(2);
    sessionStorage.setItem('sessionId', sessionId);
  }
  return sessionId;
};

/**
 * 组件挂载时添加全局错误监听
 */
onMounted(() => {
  window.addEventListener('error', handleGlobalError);
  window.addEventListener('unhandledrejection', handleUnhandledRejection);
});

/**
 * 组件卸载时移除全局错误监听
 */
onUnmounted(() => {
  window.removeEventListener('error', handleGlobalError);
  window.removeEventListener('unhandledrejection', handleUnhandledRejection);
});
</script>

<style scoped>
.error-boundary {
  min-height: 400px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.error-container {
  text-align: center;
  max-width: 600px;
  padding: 40px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

.error-icon {
  color: #f56c6c;
  margin-bottom: 20px;
}

.error-content {
  color: #606266;
}

.error-title {
  font-size: 24px;
  font-weight: 600;
  margin: 0 0 16px 0;
  color: #303133;
}

.error-message {
  font-size: 16px;
  line-height: 1.6;
  margin: 0 0 24px 0;
  color: #606266;
}

.error-details {
  margin: 20px 0;
  text-align: left;
}

.error-details summary {
  cursor: pointer;
  font-weight: 500;
  margin-bottom: 10px;
  color: #409eff;
}

.error-stack {
  background: #f5f7fa;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  padding: 12px;
  font-size: 12px;
  line-height: 1.4;
  color: #606266;
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 200px;
  overflow-y: auto;
}

.error-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

@media (max-width: 768px) {
  .error-container {
    padding: 20px;
  }
  
  .error-title {
    font-size: 20px;
  }
  
  .error-message {
    font-size: 14px;
  }
  
  .error-actions {
    flex-direction: column;
    align-items: center;
  }
  
  .error-actions .el-button {
    width: 100%;
    max-width: 200px;
  }
}
</style>
